// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <conio.h>
#include <windows.h>

#define K_UP        72
#define K_DOWN      80
#define K_LEFT      75
#define K_RIGHT     77
#define K_PGUP      73
#define K_PGDOWN    81
#define MB          (1024 * 1204)

#ifndef dimof
#define dimof(ARRAY)   (sizeof(ARRAY) / sizeof(ARRAY[0]))
#endif

char chars[2] = { 0 };
char chr;
int nchar;
void* pages[100];
int pageSizes[100];
void* page;
bool allocfailed = false;
int base = 20,
    max = 100,
    npage = 0,
    delta = 1,
    pageSize = 10 * MB,
    nUsage = 0,
    nCycle = 0;

void printMemoryUsage() {
    printf("\rMemoryUsage = [%s1] %4d MB    <- %d pages of %d MB      ", 
        delta < 0 ? "-" : "+",
        nUsage / MB, npage, pageSize / MB);
}

bool alloc() {
    if( npage >= max ) {
        printf("\n");
        //printf("Max usage = %4dMB\n", nUsage / MB);
        return false;
    }

    page = malloc(pageSize);
    if( page == 0 ) {
        printf("Failed to alloc page, total = %dMB\n", nUsage / MB);
        allocfailed = true;
        return false;
    }
    else {
        memset(page, rand(), pageSize);
        pages[npage] = page;
        pageSizes[npage] = pageSize;
        nUsage += pageSize;
        npage++;
        allocfailed = false;
        printMemoryUsage();
        return true;
    }
}

bool release() {
    if( npage <= 0 )
        return false;

    if( (npage <= base) && (pageSizes[npage - 1] == pageSize) && !allocfailed )
        return false;

    npage--;
    page = pages[npage];
    free(page);
    pages[npage] = NULL;
    nUsage -= pageSizes[npage];
    pageSizes[npage] = 0;
    printMemoryUsage();

    return true;
}

void printConfig(char* change) {
    printf("\n%-10s"
        "Base   = %d pages/%d MB, \n"
        "          Max    = %d pages/%d MB, \n"
        "          PgSize = %d MB\n", 
        change, 
        base, base * (pageSize / MB), 
        max, max * (pageSize / MB), 
        pageSize / MB);
}

int _tmain(int argc, _TCHAR* argv[])
{
    if( argc >= 2 ) {
        base = max(0, min(50, _ttoi(argv[1])));
    }

    if( argc >= 3 ) {
        max = max(base + 1, min(100, _ttoi(argv[2])));
    }

    if( argc >= 4 ) {
        pageSize = max(1, min(100, _ttoi(argv[3]))) * MB;
    }

    printf("Usage: membounce [min-page] [max-page], where page size = %dMB\n", pageSize / MB);
    printf("    UP/DOWN       +/- base (min) number of memory pages\n");
    printf("    LEFT/RIGHT    +/- max number of memory pages\n");
    printf("    PGUP/PGDN     +/- page size\n");
    printf("Press ESC to exit\n\n");

    printConfig("Startup:");

    for( int i = 0; i < base; i++, npage = i ) {
        alloc();
    }

    while( true ) {
        if( _kbhit() ) {
            chr = nchar = _getch();

            switch( nchar ) {
            case 224:
                chr = nchar = _getch();
                // printf("%d\n", chr);
                switch( chr ) {
                case K_UP:
                    if( base < max - 1 ) {
                        base ++;
                        printConfig("Base+: ");
                    }
                    break;
                case K_DOWN:
                    if( base > 0 ) {
                        base --;
                        printConfig("Base-: ");
                    }
                    break;
                case K_RIGHT:
                    if( max < dimof(pages) ) {
                        max ++;
                        printConfig("Max+: ");
                    }
                    break;
                case K_LEFT:
                    if( max > base + 1 ) {
                        max --;
                        printConfig("Max-: ");
                    }
                    break;
                case K_PGUP:
                    if( pageSize < 100 * MB ) {
                        pageSize += 1 * MB;
                        printConfig("PgSize+: ");
                    }
                    break;
                case K_PGDOWN:
                    if( pageSize > 1 * MB ) {
                        pageSize -= 1 * MB;
                        printConfig("PgSize-: ");
                    }
                    break;
                }
                break;

            case VK_ESCAPE:
                printf("ESC\n", chr);
                exit(0);
                break;

            default:
                chars[0] = chr;
                printf(chars);
            }
        }

        if( (nCycle % 10) == 0 ) {
            if( delta > 0 ) {
                if( !alloc() )
                    delta = -1;
            }
            else {
                if( !release() ) {
                    printf("\n");
                    delta = +1;
                }
            }
        }

        Sleep(100);
    }

    return 0;
}

